/***
    Amrita ITEWS - Copyright (C) 2009 Amrita Vishwa Vidyapeetham, Amritapuri.
                                       (http://www.amrita.edu)
    ***************************************************************************
    This file is part of the Amrita ITEWS distribution.
    Amrita ITEWS is free software; you can redistribute it and/or modify it 
    under the terms of the GNU General Public License (version 2) as published
    by the Free Software Foundation AND MODIFIED BY the Amrita ITEWS exception.
    ***NOTE*** The exception to the GPL is included to allow you to distribute
    a combined work that includes Amrita ITEWS without being obliged to provide
    the source code for proprietary components outside of the Amrita ITEWS
    software. Amrita ITEWS is distributed in the hope that it will be useful, 
    but WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
    Public License for more details. You should have received a copy of the GNU
    General Public License and the Amrita ITEWS license exception along with 
    Amrita ITEWS if not then it can be viewed here: 
    http://itews.amrita.ac.in/license.html.

    Documentation, latest information, license and contact details are at:
    http://itews.amrita.ac.in/

    Amrita ITEWS source code can be found at:
    http://code.google.com/p/itews

    The initial code-base of Amrita ITEWS was developed by Amrita Vishwa 
    Vidyapeetham as part of the project titled,"Intelligent & Interactive 
    Telematics using Emerging Wireless Technologies for Transport Systems 
    (ITEWS)" sponsored by Technology Information Forecasting and Assessment 
    Council (TIFAC), India.
***/
package bloggate;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Iterator;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.jdom.adapters.JAXPDOMAdapter;
import org.w3c.dom.Node;

public class UpstreamBlogGateUtility 
{
	private static UpstreamBlogGateUtility singleton = null;
	private HttpClient client;
	private final Log logger = LogFactory.getLog(getClass());	
	private DBConnectionPool dbConnectionPool ;
	private String eTagQuery = "Select last_entry_id from bloggate where feed_name = ?";
	private String eTagUpdate = "Update bloggate set last_entry_id = ? where feed_name = ?";
	private String eTagInsert = "Insert into bloggate values(default, ?, ?)";
	
	public UpstreamBlogGateUtility(HashMap serviceMap, String jdbcConnectionURI, int httpTimeOut, String user, String password) throws Exception
	{
		client = new HttpClient();
		HttpConnectionManagerParams hcp = new HttpConnectionManagerParams();
		hcp.setSoTimeout(httpTimeOut);
		hcp.setConnectionTimeout(httpTimeOut);
		HttpConnectionManager connManager = client.getHttpConnectionManager();
		connManager.setParams(hcp);
		dbConnectionPool = DBConnectionPool.getInstance(jdbcConnectionURI, user, password);
		Iterator iter = serviceMap.keySet().iterator();
		Connection conn = dbConnectionPool.getConnection();
	    	PreparedStatement preparedStmt = null;
	    	ResultSet rs = null;
	    	PreparedStatement preparedStmt1 = null;
		try
		{
			while(iter.hasNext())
	    		{
	    			String serviceName = ((String) iter.next()).trim();
				if (serviceName == null || serviceName.trim().equals(""))
				{
					logger.warn("UpstreamBlogGateUtility: Service Name not defined");
					throw new Exception();
				}
				preparedStmt = dbConnectionPool.getPreparedStmt(conn, eTagQuery);
				preparedStmt.setString(1, serviceName);
				rs = preparedStmt.executeQuery();
				if (rs.next() == false)
				{
					preparedStmt1 = dbConnectionPool.getPreparedStmt(conn, eTagInsert);
					preparedStmt1.setString(1, serviceName);
					preparedStmt1.setInt(2, 0);
					preparedStmt1.executeUpdate();
	        			preparedStmt1.close(); 
			    		preparedStmt1 = null;
				}
				rs.close(); 
				rs = null;
				preparedStmt.close(); 
				preparedStmt = null;
			}
		}
		catch (Exception e)
		{
			logger.error("Error starting UpstreamBlogGateUtility");
			e.printStackTrace();
			throw e;
		}
		finally
		{
			try 
			{ 
				if (rs != null)
				{
					rs.close();
					rs = null;
				}
				if (preparedStmt != null)
				{
					preparedStmt.close();
					preparedStmt = null;
				}
				if (preparedStmt1 != null)
				{
					preparedStmt1.close();
					preparedStmt1 = null;
				}
				if (conn != null)
				{
					conn.close();
					conn = null;
				}
			} 
			catch (Exception e) {}
		}
	}
	protected static UpstreamBlogGateUtility getInstance(HashMap serviceMap, String jdbcConnectionURI, int httpTimeOut, String user, String password) throws Exception
	{
		if (singleton == null)
		{
			singleton = new UpstreamBlogGateUtility(serviceMap, jdbcConnectionURI, httpTimeOut, user, password);
		}
		return singleton;
	}
	
    protected synchronized void enableUpStreamService(HashMap serviceMap, String rootBlogCacheUrlBase)
    {
    	Connection conn = null;
    	PreparedStatement preparedStmt = null;
    	ResultSet rs = null;
    	PreparedStatement preparedStmt1 = null;
    	GetMethod rootBlogCacheGet = null;
    	String feedURI = null;
		String content = null;
    	try 
		{
	    	Iterator iter = serviceMap.keySet().iterator();
	    	while(iter.hasNext())
	    	{
	    		String serviceName = ((String) iter.next()).trim();
				if (serviceName == null || serviceName.trim().equals(""))
				{
					logger.warn("UpstreamBlogGateUtility: Service Name not defined");
					throw new Exception();
				}
				String serviceURL = (String) serviceMap.get(serviceName);
				feedURI = rootBlogCacheUrlBase+serviceName;
				rootBlogCacheGet = new GetMethod(feedURI);
				conn = dbConnectionPool.getConnection();
				preparedStmt = dbConnectionPool.getPreparedStmt(conn, eTagQuery);
				preparedStmt.setString(1, serviceName);
				rs = preparedStmt.executeQuery();
				Long oldETag = 0L;
				if (rs.next() == true)
				{
					oldETag = rs.getLong("last_entry_id");
				}
				rs.close(); 
				rs = null;
				preparedStmt.close(); 
				preparedStmt = null;
				rootBlogCacheGet.addRequestHeader(new Header("If-None-Match", String.valueOf(oldETag)));
				rootBlogCacheGet.addRequestHeader(new Header("User-Agent", "blogate"));
				client.executeMethod(rootBlogCacheGet);

				if (rootBlogCacheGet.getStatusCode() == HttpStatus.SC_OK) 
			    {
					Header eTagHeader = rootBlogCacheGet.getResponseHeader("ETag");
					int eTagValue = 0;
					if (eTagHeader != null)
					{
						eTagValue = Integer.parseInt(eTagHeader.getValue());
					}
					else
					{
						logger.warn("Etag Value is not set in HTTP Header for service "+serviceName);
						rootBlogCacheGet.releaseConnection(); rootBlogCacheGet = null;
						conn.close(); 
						conn = null;
						return;
					}
		        	InputStream istream = rootBlogCacheGet.getResponseBodyAsStream();
					JAXPDOMAdapter domAdapter = new JAXPDOMAdapter();
					Document doc = domAdapter.getDocument(istream, false);
				  	Node feed = doc.getFirstChild();
		        	NodeList nodeList = feed.getChildNodes();
		        	for (int i=0; i< nodeList.getLength(); i++)
		        	{
		        		Node node = nodeList.item(i);

		        		if (node.getNodeName().equals("entry"))
		        		{
		        			NodeList childList = node.getChildNodes();
		        			String authorName = null;
		        			for(int j=0;j<childList.getLength();j++)
		        			{
		        				Node child = childList.item(j);
		        				if (child.getNodeName().equals("author"))
		        				{
		        					authorName = child.getTextContent();
			        				if (authorName == null)
			        				{
			        					logger.warn("Author Name is null for service "+serviceName);
			        				}
		        				}
		        				if (child.getNodeName().equals("content"))
		        				{
		        					content = child.getTextContent();
		        					logger.info("Content received at Blog Gate: "+content);
		        				}
		        				if (authorName != null & content != null)
		        				{
		        					boolean postStatus = postContentToServiceApp(content, authorName, serviceURL);
		        					if (postStatus == false)
			    					{
			    						logger.warn("Post to Service App failed: "+serviceURL + " Content: " + content);
			    					}
			    					else
			    					{
			    						logger.info("Posted to Service App: "+serviceURL);
			    				        	preparedStmt1 = dbConnectionPool.getPreparedStmt(conn, eTagUpdate);
			    				        	preparedStmt1.setInt(1, eTagValue);
			    				        	preparedStmt1.setString(2, serviceName);
			    				        	preparedStmt1.executeUpdate();
				    			        	preparedStmt1.close(); 
				    			        	preparedStmt1 = null;
			    					}
		        					authorName = null; content = null;
		        				}

		        			}
		        		}
		        	}
			    }
				else if (rootBlogCacheGet.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) 
				{
					logger.info("UpstreamBlogGateUtility: NOT modified Status: " + feedURI);
				}
				else if (rootBlogCacheGet.getStatusCode() == HttpStatus.SC_REQUEST_TIMEOUT |
						rootBlogCacheGet.getStatusCode() == HttpStatus.SC_GATEWAY_TIMEOUT |
						rootBlogCacheGet.getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE) 
				{
					logger.error("Upstream : HTTP error while getting content from Root Blog Cache: " + feedURI + " Status Code: "+rootBlogCacheGet.getStatusCode());
				}
				else
				{
					logger.error("Upstream : HTTP error while getting content from Root Blog Cache "+feedURI + " Status Code: " +rootBlogCacheGet.getStatusCode());
				}
				rootBlogCacheGet.releaseConnection(); rootBlogCacheGet = null;
				conn.close(); conn = null;
	    	}
		} 
		catch (Exception e)
		{
			logger.error("Error during Upstream GET from Root blog Cache and POST to Service Application: "+ feedURI +" "+ e + " Content: "+ content);
			try 
			{
				if (rootBlogCacheGet != null)
					logger.info(rootBlogCacheGet.getResponseBodyAsString());
			} catch (IOException e1) {}
			//e.printStackTrace();
		}
		finally
		{
			try 
			{ 
				if (rs != null)
				{
					rs.close();
					rs = null;
				}
				if (preparedStmt != null)
				{
					preparedStmt.close();
					preparedStmt = null;
				}
				if (preparedStmt1 != null)
				{
					preparedStmt1.close();
					preparedStmt1 = null;
				}
				if (conn != null)
				{
					conn.close();
					conn = null;
				}
				if (rootBlogCacheGet != null)
				{
					rootBlogCacheGet.releaseConnection();
					rootBlogCacheGet = null;
				}
			} 
			catch (Exception e) {}
		}
    }
    
    private synchronized boolean postContentToServiceApp(String content, 
    												  String authorName,
    												  String serviceURL) throws Exception
    {
    	boolean postStatus = false;
    	try
    	{
	    	ByteArrayInputStream is = new ByteArrayInputStream(content.getBytes());
			InputStreamRequestEntity requestEntity = new InputStreamRequestEntity(is);
			PostMethod servicePost = new PostMethod(serviceURL);
			servicePost.addRequestHeader(new Header("X-TU-List", authorName.trim()));
			servicePost.setRequestEntity(requestEntity);

			client.executeMethod(servicePost);

			if (servicePost.getStatusCode() == HttpStatus.SC_OK)
			{
				postStatus = true;
			}
			else
			{
				logger.error("HTTP Response: "+ servicePost.getStatusCode() + " " +servicePost.getResponseBodyAsString());
			}
			
			servicePost.releaseConnection();
			return postStatus;
    	}
    	catch (Exception e)
    	{
    		logger.error("UpstreamBlogGateUtility: Error while posting to serviceURL: "+serviceURL + " " +e + " Content: "+content);
    		//e.printStackTrace();
    		return postStatus;
    	}
    }
}
